package it.antonio.ilpizze.ioc.builders;

import it.antonio.ilpizze.ioc.Builder;
import it.antonio.ilpizze.ioc.Construction;
import it.antonio.ilpizze.ioc.Decoration;
import it.antonio.ilpizze.ioc.Dependencies;
import it.antonio.ilpizze.ioc.InjectionMonitor;
import it.antonio.ilpizze.ioc.IocException;
import it.antonio.ilpizze.ioc.Scope;
import it.antonio.ilpizze.ioc.Transformation;

import java.util.List;

public class ObjectBuilder implements Builder<Object> {
	
	private Class<? extends Object> type;
	private Object key;
	
	private Scope<Object> scope;
	private Construction<Object> construction;
	private Transformation<Object> transformation;
	private List<Decoration<Object>> decorations;
	private List<InjectionMonitor> monitors;
		
	public ObjectBuilder(Class<? extends Object> type, Object key, Scope<Object> scope, Construction<Object> construction, Transformation<Object> transformation, List<Decoration<Object>> decorations, List<InjectionMonitor> monitors) {
		this.type = type;
		this.key = key;
		this.scope = scope;
		this.construction = construction;
		this.transformation = transformation;
		this.decorations = decorations;
		this.monitors = monitors;
	
	}

	@Override
	public Class<? extends Object> getType() {
		return type;
	}

	@Override
	public Object getKey() {
		return key;
	}
	
	
	public final Object build(Dependencies dependencies) {
		
		
		for(InjectionMonitor monitor: monitors){
			monitor.before(getKey(),getType());
		}
		
		
		Object instance = scope.get();
		if(instance != null){
			return instance;
		} else {
			if(construction == null){
				throw new IocException("No Construction defined for component with key " + getKey() + " of type " + getType());
			}
			instance = construction.construct(type, dependencies);
			instance = transformation.transform(instance);
			for (Decoration<Object> decoration : decorations) {
				decoration.decorate(instance, dependencies);
			}
			scope.set(instance);
		}
		for(InjectionMonitor monitor: monitors){
			monitor.after(getKey(),getType());
		}
		return instance;
	}

	@Override
	public String toString() {
		return "ObjectBuilder[" + getKey() + ":" + getType().getSimpleName() + "]";
	}

}
